/**
 * Copyright 2011 Honeywell Federal Manufacturing & Technologies, LLC. This material was produced under U.S. Government
 * contract DE-ACO4-01AL66850, Honeywell Federal Manufacturing & Technologies, LLC, (FM&T) with the U. S. Department of
 * Energy for the operation of the Kansas City Plant . The U.S. Government has rights to use, reproduce, and distribute
 * this software.  NEITHER THE GOVERNMENT NOR Honeywell Federal Manufacturing & Technologies, LLC, MAKES ANY WARRANTY,
 * EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.  If software is modified to produce
 * derivative works, such modified software should be clearly marked, so as not to confuse it with the version available
 * from FM&T.
 *
 * Additionally, this program is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License Version 3 published by the Free Software Foundation. Accordingly, this program is distributed in
 * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this library.
 * If not, see http://www.gnu.org/licenses/.
 */
package org.braintrain.tapestry.view;

import com.kcp.ko.fmtnmlib.tapestry.form.IntegerRangePropertySelectionModel;
import org.apache.tapestry.IAsset;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.annotations.Asset;
import org.apache.tapestry.annotations.Bean;
import org.apache.tapestry.annotations.InjectPage;
import org.apache.tapestry.annotations.InjectState;
import org.apache.tapestry.form.IFormComponent;
import org.apache.tapestry.form.IPropertySelectionModel;
import org.apache.tapestry.valid.ValidationDelegate;
import org.braintrain.domain.Answer;
import org.braintrain.domain.Question;
import org.braintrain.domain.TestData;
import org.braintrain.tapestry.aso.TestASO;
import org.braintrain.tapestry.model.PercentagePropertySelectionModel;

/**
 * Tapestry EditPage page.  Allows the user to edit a given test and save the test into
 * the navigation chain.
 * 
 * @author John Ericksen
 *
 */

public abstract class EditTest extends BrainTrainEditableNavigatablePage {	 

    //injections
    @Asset("/images/add.gif")
    public abstract IAsset getAddImage();
    
    @Asset("/images/close.gif")
    public abstract IAsset getDeleteImage();
    
    @InjectState("testaso")
    public abstract TestASO getTestASO();
    
    @Bean
    public abstract ValidationDelegate getDefaultDelegate();
    
    @InjectPage("Test")
    public abstract Test getTestPage();
    
    //  loop variables
    public abstract Question getQuestion();
    public abstract void setQuestion(Question q);
    
    public abstract Answer getAnswer();
    public abstract void setAnswer(Answer a);
    
    private int numberOfQuestionsToAdd = 1;
    
    public int getNumberOfQuestionsToAdd() {
        return numberOfQuestionsToAdd;
    }
    public void setNumberOfQuestionsToAdd(int numberOfQuestionsToAdd) {
        this.numberOfQuestionsToAdd = numberOfQuestionsToAdd;
    }
    
    /**
     * parsing methods for getting and setting integers from a string field
     * 
     * @return
     */
    public String getNumberingStart()
    {
        return Integer.toString(getTestData().getNumberStartAt());
    }
    
    public void setNumberingStart(String start)
    {
        try
        {
            getTestData().setNumberStartAt(Integer.parseInt(start));
        }
        catch(NumberFormatException e)
        {
            getDefaultDelegate().record((IFormComponent)getComponent("numberingStart"), "You must supply a valid number in the 'Test numbering starts at' field");
        }
    }
    
    /**
     * Returns the current test data from the nativation
     * 
     * @return
     */
    public TestData getTestData()
    {
        TestData testData = getTestASO().getEditTest(getNavId());
        if(testData == null)
        {
            testData = (TestData)getNavigation().getEditableContent();
            getTestASO().putEditTest(testData, getNavId());
        }
        return testData;
    }

    /**
     * Initializes the editing process:
     * 1. Copies off an editable version of the test
     * 2. Puts the copied test into the Test ASO for editing purposes
     */
    public void init(){
        TestData testData = (TestData)getNavigation().getEditableContent();
        
        getTestASO().putEditTest(testData, getNavId());
    }
    
    /**
     * Does the work of saving the test with validation of test answers
     * 
     * @param cycle
     */
    public void saveAndContinueSubmit(IRequestCycle cycle)
    {
        if(getNavigation() != null)
        {
            //make sure the number of questions being used is never more than the number to continue on correct
            if(getTestData().getNumberCorrectToPass() > getTestData().getNumberOfQuestionsToUse())
            {
                getTestData().setNumberCorrectToPass(getTestData().getNumberOfQuestionsToUse());
            }
            //validate single select question answers and remove all correct booleans on answers that are empty
            for(Question question: getTestData().getQuestions())
            {
                for(Answer answer: question.getAnswers())
                {
                    if(answer.getAnswer() == null || answer.getAnswer().trim().length() == 0)
                    {
                        answer.setCorrect(false);
                    }
                }
                if(!question.isMultipleAnswer())
                {
                    boolean correctFound = false;
                    for(Answer answer: question.getAnswers())
                    {
                        if(correctFound)
                        {
                            answer.setCorrect(false);
                        }
                        else if(answer.isCorrect())
                        {
                            correctFound = true;
                        }
                    }
                    
                    if(!correctFound && question.getAnswers().size() > 0)
                    {
                        //ERROR, no correct answer, set the first "non empty" answer as the correct one
                        for (Answer answer : question.getAnswers()) {
                            if (answer.getAnswer() != null && answer.getAnswer().trim().length() > 0) {
                                answer.setCorrect(true);
                                break;
                            }
                        }
                    }
                }
            }
             
            if(!getDefaultDelegate().getHasErrors())
            {
                
                getNavigation().setName(getTestData().getName());
                getNavigation().getNullSafeContent().setDeltaValue(getTestData());
                getGenericService().saveOrUpdate(getNavigation());
            }
        }
    }
    

    /**
     * Calls the saveAndContinueSubmit() function and returns to the view page
     * 
     * @param cycle
     */
    public void saveSubmit(IRequestCycle cycle)
    {
        if(getNavigation() != null)
        {
            saveAndContinueSubmit(cycle);
            if(!getDefaultDelegate().getHasErrors())
            {
                getTestData().setupTest();
                getTestASO().putTest(getTestData(), getNavId());
                getTestPage().activate(cycle, getNavId(), getParentNavId());
            }
        }
        //todo:redirect
    }
    
    /**
     * Cancels any pending changes and returns to the view page
     * 
     * @param cycle
     */
    public void cancelSubmit(IRequestCycle cycle)
    {
        if(getNavId() != null)
        {
            getTestPage().activate(cycle, getNavId(), getParentNavId());
        }
        else
        {
            cycle.activate(getHomePage());
        }
        //todo:redirect
    }
    
    /**
     * Add a question(s) to the end of the test taking into account the
     * difference in 'number correct to pass' and 'number of questions to use'
     * 
     * @param cycle
     */
    public void addQuestion(IRequestCycle cycle)
    {
        for(int i = 0; i < numberOfQuestionsToAdd; i++)
        {
            Question questions = new Question(4);
            getTestData().getQuestions().add(questions);
        }
        
        getTestData().setNumberCorrectToPass(getTestData().getNumberCorrectToPass() + numberOfQuestionsToAdd);
        getTestData().setNumberOfQuestionsToUse(getTestData().getNumberOfQuestionsToUse() + numberOfQuestionsToAdd);
        
        getTestData().resetIndexes();
        setNumberOfQuestionsToAdd(1);
    }
    
    /**
     * Remove a question from the test taking into account the
     * difference in 'number correct to pass' and 'number of questions to use'
     * 
     * @param cycle
     */
    public void deleteQuestion(IRequestCycle cycle)
    {
        Object[] params = cycle.getListenerParameters();
        
        if(params.length > 0 && params[0] instanceof Integer)
        {
            getTestData().getQuestions().remove(((Integer)params[0]).intValue());
            
            getTestData().resetIndexes();
            
            if(getTestData().getNumberCorrectToPass() > 0)
                getTestData().setNumberCorrectToPass(getTestData().getNumberCorrectToPass() - 1);
            if(getTestData().getNumberOfQuestionsToUse() > 1)
            {
                if(getTestData().getNumberCorrectToPass() > getTestData().getNumberOfQuestionsToUse())
                {
                    getTestData().setNumberCorrectToPass(getTestData().getNumberOfQuestionsToUse());
                }
                else
                {
                    getTestData().setNumberOfQuestionsToUse(getTestData().getNumberOfQuestionsToUse() - 1);
                }
            }
            else if(getTestData().getQuestions().size() == 0)
            {
                getTestData().setNumberOfQuestionsToUse(0);
            }
        }  
    }
    
    /**
     * Remove a given answer from a given question/answer pair
     * 
     * @param cycle
     */
    public void deleteAnswer(IRequestCycle cycle) {
        Object[] params = cycle.getListenerParameters();
        
        if(params.length > 1 && params[0] instanceof Integer && params[1] instanceof Integer) {
            getTestData().getQuestions().get((Integer)params[0]).getAnswers().remove(((Integer)params[1]).intValue());
        
            getTestData().resetIndexes();
        }
    }
    
    /**
     * Add an answer to a given question
     * @param cycle
     */
    public void addAnswer(IRequestCycle cycle) {
        Object[] params = cycle.getListenerParameters();
        
        if(params.length > 0 && params[0] instanceof Integer) {
            Question currentQuestion = getTestData().getQuestions().get((Integer)params[0]);
            
            currentQuestion.getAnswers().add(new Answer());
            getTestData().resetIndexes();
        }
    }
    
    /**
     * Property Selection Models
     */
    public IPropertySelectionModel getNumberOfQuestionsPSModel() {
        return new IntegerRangePropertySelectionModel(
                getTestData().getQuestions().size() == 0? 0 : 1
              , getTestData().getQuestions().size());
    }

    public IPropertySelectionModel getNumberToPassPSModel() {
        return new PercentagePropertySelectionModel(0, getTestData().getQuestions().size());
    }
    
    public IPropertySelectionModel getNumberOfQuestionsToAddPSModel() {
        return new IntegerRangePropertySelectionModel(1, 10);
    }
    
   
}
